/*******************************************************************************
 * Copyright (c) 2000, 2015 IBM Corporation and others.
 *
 * This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License 2.0
 * which accompanies this distribution, and is available at
 * https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *     Francis Lynch (Wind River) - [301563] adapted from RefreshLocalPerformanceTest
 *******************************************************************************/
package org.eclipse.core.tests.resources;

import static org.eclipse.core.resources.ResourcesPlugin.getWorkspace;
import static org.eclipse.core.tests.resources.ResourceTestUtil.createInWorkspace;
import static org.junit.Assert.assertTrue;

import java.io.File;
import java.net.URI;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IProjectDescription;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IResourceDelta;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.tests.harness.PerformanceTestRunner;
import org.junit.Assume;
import org.junit.Rule;
import org.junit.Test;

/**
 * Measure speed of a project "Import with Snapshot" operation compared
 * to a normal "Project Import" operation, as per bug 301563.
 *
 * This test is meant to be run manually, and not integrated into the
 * automated test suite. Requires large project data (thousands of folders)
 * to be available on a slow file system (bigSiteLocation). Modify
 * bigSiteLocation to suit your needs, then run-as &gt; JUnit Plug-in Test.
 */
public class ProjectSnapshotPerfManualTest {

	@Rule
	public WorkspaceTestRule workspaceRule = new WorkspaceTestRule();

	/** big site default volume (windows) */
	public static final String bigSiteDevice = "c:";

	/** big site initial location. Modify to suit your needs */
	public static final IPath bigSiteLocation = new Path(bigSiteDevice, "/test");

	/** settings directory name */
	private static final String DIR_NAME = ".settings";

	/** location of refresh snapshot file */
	private static final String REFRESH_SNAPSHOT_FILE_LOCATION = ".settings/resource-index.zip";

	protected int countChildren(File root) {
		String[] children = root.list();
		if (children == null) {
			return 0;
		}
		int result = 0;
		for (String element : children) {
			File child = new File(root, element);
			if (child.isDirectory()) {
				result += countChildren(child);
			}
			result++;
		}
		return result;
	}

	/**
	 * Open a project and export a refresh snapshot. Re-open the project using
	 * the snapshot and compare the times for opening with and without the
	 * snapshot.
	 */
	@Test
	public void testSnapshotImportPerformance() throws Exception {
		// test if the test can be done in this machine
		Assume.assumeTrue(bigSiteLocation.toFile().isDirectory());

		// create common objects
		final IProject project = getWorkspace().getRoot().getProject("MyTestProject");
		IProjectDescription description = getWorkspace().newProjectDescription(project.getName());
		description.setLocation(bigSiteLocation);

		// report the number of files to be refreshed
		int numberOfFiles = countChildren(bigSiteLocation.toFile());
		System.out.println("Number of local resources: " + numberOfFiles);

		// create the project from the location
		project.create(description, null);

		// open the project, timing the initial refresh
		new PerformanceTestRunner() {
			@Override
			protected void test() throws CoreException {
				project.open(null);
			}
		}.run(getClass(), "Original open", 1, 1);

		// dump the snapshot refresh info
		createInWorkspace(project.getFolder(DIR_NAME));
		IPath projPath = project.getLocation();
		projPath = projPath.append(REFRESH_SNAPSHOT_FILE_LOCATION);
		final URI snapshotLocation = org.eclipse.core.filesystem.URIUtil.toURI(projPath);
		project.saveSnapshot(IProject.SNAPSHOT_TREE, snapshotLocation, null);

		// close and delete project but leave contents
		project.close(null);
		project.delete(false, false, null);

		// open the project and import refresh snapshot
		project.create(description, null);
		new PerformanceTestRunner() {
			@Override
			protected void test() throws CoreException {
				project.loadSnapshot(IProject.SNAPSHOT_TREE, snapshotLocation, null);
				project.open(IResource.NONE, null);
			}
		}.run(getClass(), "Snapshot open", 1, 1);

		// now refresh the project, verifying zero resource delta
		// (except for the creation of .settings/resource-index.zip)
		final ResourceDeltaVerifier[] verifier = new ResourceDeltaVerifier[1];
		new PerformanceTestRunner() {
			@Override
			protected void test() throws CoreException {
				verifier[0] = new ResourceDeltaVerifier();
				ResourcesPlugin.getWorkspace().addResourceChangeListener(verifier[0]);
				verifier[0].reset();
				IFolder settings = project.getFolder(DIR_NAME);
				IFile snapshot = project.getFile(REFRESH_SNAPSHOT_FILE_LOCATION);
				verifier[0].addExpectedChange(settings, IResourceDelta.CHANGED, 0);
				verifier[0].addExpectedChange(snapshot, IResourceDelta.ADDED, 0);
				project.refreshLocal(IResource.DEPTH_INFINITE, null);
			}
		}.run(getClass(), "Forced refresh only", 1, 1);
		verifier[0].verifyDelta(null);
		assertTrue("1.0 " + verifier[0].getMessage(), verifier[0].isDeltaValid());

		// close and delete project but leave contents
		project.close(null);
		project.delete(false, false, null);
		IPath snapshotFile = bigSiteLocation.append(REFRESH_SNAPSHOT_FILE_LOCATION);
		snapshotFile.toFile().delete();

		// open the project again with standard refresh
		project.create(description, null);
		new PerformanceTestRunner() {
			@Override
			protected void test() throws CoreException {
				project.open(null);
			}
		}.run(getClass(), "Second refresh open", 1, 1);

		// delete project to avoid getting content deleted in tearDown()
		project.close(null);
		project.delete(false, false, null);
	}

}
